/*************************************************************
*
* M3D_MISC.CPP
*
* Miscellaneous (rarely used functions)
*************************************************************/
#include <stdafx.h>

//#include <ap_comm.h>
#include <m3d/m3d.h>

#include <stdlib.h>

extern void _m3xzDist2PointPoly (m3dV *point, m3dPOLY *poly, m3dV *vClosest, float *dist);


/***************************************************************
* m3dGetCirclePoints ()
*
***************************************************************/
void m3dGetCirclePoints (m3dV const*org, m3dV const*norm, float radius, int nPoints, m3dV *pointList)
{
   m3dV    perp;
   float   step;
   int     i;

   m3dGetPerpVector(norm, &perp);
   m3dNormalize(&perp);
   _m3dLineCombine(org, &perp, radius, &pointList[0]);

   step = 360.f / nPoints;
   for (i = 1; i < nPoints; i ++) {
      m3dRotatePointAroundRay(org, norm, i * step, &pointList[0], &pointList[i]);
   }
   return;
}

/*************************************************************
* m3dGetClosestVert ()
*
*************************************************************/
int m3dGetClosestVert (int nVert, m3dV *vertList, m3dV *v)
{
   int   i, ind;
   float dist, minDist;

   ind     = -1;
   minDist = M3D_INFINITY;
   for (i = 0; i < nVert; i ++) {
      dist = m3dDist(v, &vertList[i]);
      if (dist < minDist) {
         minDist = dist;
         ind     = i;
      }
   }
   return ind;
}
/*************************************************************
* _m3xzIsIsectPolyCircle ()
*
* IN:  poly   - polygon (in 3D-XZ)
*      org    - circle origin
*      radius - circle radius
* RET: TRUE  - polygon and circle do intersect
*      FALSE - otherwise
* 
* Intersect a polygon with a circle.
* NOTE: It is known that the sphere origin is outside the polygon
*************************************************************/
BOOL _m3xzIsIsectPolyCircle (m3dPOLY *poly, m3dV *org, float radius)
{
   float    t;
   int      i;
   float    radius_2 = m3dPow2(radius);
   m3dV    *lB, *lE, dir, v;

   // NOTE: It is known that the sphere origin is outside the polygon
   ASSERT(!m3xzIsBelongPointPoly(poly, org));
   
   for (i = 0; i < m3dPolyNVert(poly); i++) {
      if (m3xzDist_2(m3dPolyVertex(poly, i), org) < radius_2) {
         return TRUE;
      }
   }

   for (i = 0; i < m3dPolyNVert(poly) - 1; i++) {
      lB = m3dPolyVertex(poly, i);
      lE = m3dPolyVertex(poly, i+1);
      m3dSubtractVector(lE, lB, &dir);
      t = ((org->x - lB->x)*dir.x + (org->z - lB->z)*dir.z)/(m3dPow2(dir.x) + m3dPow2(dir.z));
      if (t > 0.f && t < 1.f) {
         _m3dLineCombine(lB, &dir, t, &v);
         if (m3xzDist_2(&v, org) < radius_2) {
            return TRUE;
         }
      }
   }

   lB = m3dPolyVertex(poly, i);
   lE = m3dPolyVertex(poly, 0);
   m3dSubtractVector(lE, lB, &dir);
   t = ((org->x - lB->x)*dir.x + (org->z - lB->z)*dir.z)/(m3dPow2(dir.x) + m3dPow2(dir.z));
   if (t > 0.f && t < 1.f) {
      _m3dLineCombine(lB, &dir, t, &v);
      if (m3xzDist_2(&v, org) < radius_2) {
         return TRUE;
      }
   }
   return FALSE;
}

/*************************************************************
* m3xzIsIsectPolyCircle ()
*
* IN: poly   - polygon that might be shaded
*     org    - shadow origin
*     radius - shadow radius
*
* Check if polygon isects a given circle
*************************************************************/
BOOL m3xzIsIsectPolyCircle (m3dPOLY *poly, m3dV *org, float radius)
{
   m3dBOX  bbPoly;
   m3dV    bbCenter;

   // check if this face must be shaded by the given shadow source
   if (!m3xzIsBelongPointPoly(poly, org))  {
      // point of sphere origin projection does not belong to polygon
      // perform more checks to avoid shading of those polygons
      // that are not touched by this shadow source

      // TEST-I: find the bbox of the tranPoly and check 
      //         if it's enclosing circle intersects
      //         the shadow projection on the polygon plane
      
      // find bbox of the polygon
      bbPoly.CalcXZ(m3dPolyVertList(poly), m3dPolyNVert(poly), 0.f);
      bbCenter.x = (bbPoly.bll.x + bbPoly.fur.x)*0.5f;
      bbCenter.z = (bbPoly.bll.z + bbPoly.fur.z)*0.5f;

      // check the circle intersection
      if (m3xzDist(&bbCenter, org) > 
          radius + m3xzDist(&bbCenter, &bbPoly.bll)) {
         return FALSE;
      }

      // TEST-II: more thourough test. Find the distance-XZ from the
      //          shadow origin and the polygon. If it is more then
      //          radShadow, polygon must not be shaded
      if (!_m3xzIsIsectPolyCircle(poly, org, radius)) {
         return FALSE;
      }
   }
   return TRUE;
}

/************************************************************
* m3xzDistPointPoly()
*
* Calculate the distance between point and polygon.
************************************************************/
void m3xzDistPointPoly (m3dV *point, m3dPOLY *poly, m3dV *vClosest, float *dist)
{
   if (m3xzIsBelongPointPoly(poly, point)) {
      if (vClosest) {
         *vClosest = *point;
      }
      if (dist) {
         *dist = 0.f;
      }
      return;
   }
   _m3xzDist2PointPoly(point, poly, vClosest, dist);
   if (dist) {
      *dist = m3dSqrt(*dist);
   }
   return;
}

/************************************************************
* _m3xzDist2PointPoly()
*
* Calculate the squared distance between point and polygon.
* It is assumed that the point does not belong to the polygon
* in projection onto XZ
************************************************************/
void _m3xzDist2PointPoly (m3dV *point, m3dPOLY *poly, m3dV *vClosest, float *dist)
{
   m3dV     v, vMin;
   float    d_2, dMin_2;
   int      i;
   BOOL     rc;

   ASSERT(!m3xzIsBelongPointPoly(poly, point));

   // point is not projected onto the surface of the polygon
   // calculate the minimal distance between the point and 
   // sides of polygon
   rc = m3xzDistPointLSeg_2(point, m3dPolyVertex(poly, m3dPolyNVert(poly)-1), m3dPolyVertex(poly,0), M3D_INFINITY, &dMin_2, &vMin);
   ASSERT(rc == TRUE);
   for (i = 0; i < m3dPolyNVert(poly)-1; i++ )  {
      if (m3xzDistPointLSeg_2(point, m3dPolyVertex(poly,i), m3dPolyVertex(poly,i+1), dMin_2, &d_2, &v)) {
         ASSERT(d_2 < dMin_2 + 0.001f);
         dMin_2 = d_2;
         vMin = v;
      }
   }

   if (dist) {
      *dist = dMin_2;
   }
   if (vClosest) {
      *vClosest = vMin;
   }

   return ;
}
/************************************************************
* m3dQualVListPlane ()
* 
*    Qualify vertex list by plane
************************************************************/
int m3dQualVListPlane (m3dV *planePoint, m3dV *planeNormal, int nVert, m3dV *vList)
{
   int     i, code;
   float   dist;

   code = 0;
   for (i = 0; i < nVert; i ++) {
      dist = m3dDistPointPlane_S(&vList[i], planePoint, planeNormal);
      if (dist >= 0.f) {
         code |= M3D_CODE_ABOVE;
      } else {
         code |= M3D_CODE_BELOW;
      } 
   }
   if (_IsState(code, M3D_CODE_ABOVE) && _IsState(code, M3D_CODE_BELOW)) {
      return M3D_ISECT;
   }
   if (_IsState(code, M3D_CODE_ABOVE)) {
      return M3D_INSIDE;
   }
   return M3D_OUTSIDE;
}
//
// End-of-file M3D_MISC.CPP
//
